Urban Efficiency vs. Suburban Scarcity
Using the latest SEPTA General Transit Feed Specification (GTFS)
data, it
reveals that a staggering 80% of SEPTA’s trips are made by bus,
showcasing the critical role of bus transit in Philadelphia’s
transportation network. This statistic underlines the heavy reliance on
buses across the city, not just in suburban areas.
# Import GTFS data
stops <- read.csv("data/google_bus/stops.txt", header = TRUE, sep = ",")
stop_times <- read.csv("data/google_bus/stop_times.txt", header = TRUE, sep = ",")
trips <- read.csv("data/google_bus/trips.txt", header = TRUE, sep = ",")
shapes <- read.csv("data/google_bus/shapes.txt", header = TRUE, sep = ",")
routes <- read.csv("data/google_bus/routes.txt", header = TRUE, sep = ",")
calendar <- read.csv("data/google_bus/calendar.txt", header = TRUE, sep = ",")
calendar_dates <- read.csv("data/google_bus/calendar_dates.txt", header = TRUE, sep = ",")
# Import Swiftly data
swiftly <- st_read("data/SwiftlyShapes/geoJSONs/SEPTASurface_HighResSpeed_v2.json")
# Join stop_times and trips, from now on dat will be our main data set
dat <- left_join(stop_times, trips, by = "trip_id")
# join dat and routes
dat <- left_join(dat, routes, by = "route_id")
#determine route_type
dat <- dat %>%
mutate(mode = if_else(route_type == 0, "Trolley", ifelse(route_type == 1, "Metro", ifelse(route_type == 3, "Bus", "Trolleybus"))))
# Mode share (not ridership)
dat_trip_by_mode <- dat %>%
group_by(mode) %>%
summarise(mode_count = n(), .groups = 'drop') %>%
mutate(total_count = sum(mode_count),
mode_share = round(mode_count/sum(mode_count)*100, digits = 2)) %>%
ungroup()
# Plot mode share using ggplot2 and the custom palette
ggplot(dat_trip_by_mode, aes(x = mode, y = mode_share, fill = mode)) +
geom_bar(stat = "identity", width = 0.75) +
scale_fill_manual(values = bus_revolution_palette(length(unique(dat_trip_by_mode$mode)))) +
geom_text(aes(label = paste0(mode_share, "% (", mode_count, " trips)")),
vjust = -0.5, color = "black", size = 2.5) +
labs(title = "Mode Share by Transit Type",
x = "Mode",
y = "Mode Share (%)",
fill = "Mode") +
theme_minimal() +
theme(legend.position = "right",
plot.title = element_text(hjust = 0.5))

# total trip bus & trolley bus 1.980.505, total observations 2.026.677
The data also highlights distinct patterns in ridership, with bus
usage peaking during morning (6-9AM) and evening (3-6PM) rush hours on
weekdays. This contrasts with weekends, where the pattern flattens,
indicating a more even distribution of trips throughout the day.
# CREATE LINE GRAPH OF TRIPS BY SERVICE PERIODS
# Function to normalize times greater than 24:00:00. Important step: check the stop_times$arrival_time and departure_time values if it's greater than 24:00
normalize_time <- function(time_str) {
time_parts <- strsplit(time_str, ":")[[1]]
hours <- as.numeric(time_parts[1])
minutes <- as.numeric(time_parts[2])
seconds <- as.numeric(time_parts[3])
if (hours >= 24) {
hours <- hours - 24
time_str <- sprintf("%02d:%02d:%02d", hours, minutes, seconds)
}
return(time_str)
}
#identify peak_pm category
dat <- dat %>%
mutate(departure_time = sapply(departure_time, normalize_time), # departure_time's normalized
departure_time = lubridate::hms(departure_time),
hour = hour(departure_time), # Extract hour component for easier condition checking
peak_category = if_else(hour >= 4 & hour < 6, "Early Morning",
if_else(hour >= 6 & hour < 9, "AM Peak",
if_else(hour >= 9 & hour < 15, "Mid Day",
if_else(hour >= 15 & hour < 18, "PM Peak",
if_else(hour >= 18 & hour < 22, "Evening",
ifelse(hour >= 22 & hour < 24, "Late Night", "Owl")))))))
# # Check if there are any NA values in the 'departure_time' column
# summarise(dat,
# na_count = sum(is.na(departure_time)), #Count NA values
# total_rows = n(), # Total number of rows for context
# percentage_na = na_count / total_rows * 100 # Percentage of NA values
# )
durations <- c("Owl" = 4, "Early Morning" = 2, "AM Peak" = 3, "Mid Day" = 6, "PM Peak" = 3, "Evening" = 4, "Late Night" = 2)
dat_service_period <- dat %>%
filter(mode %in% c("Bus", "Trolleybus", "Trolley")) %>%
group_by(peak_category)%>%
summarise(peak_count = n(), .groups = 'drop') %>%
mutate(total_count = sum(peak_count),
peak_share = round(peak_count/sum(peak_count)*100, digits = 2)) %>%
arrange(factor(peak_category, levels = c("Owl", "Early Morning", "AM Peak", "Mid Day", "PM Peak", "Evening", "Late Night"))) %>%
ungroup()
# Create a data frame for service periods
service_periods <- data.frame(
peak_category = c("Owl", "Early Morning", "AM Peak", "Mid Day", "PM Peak", "Evening", "Late Night"),
start_hour = c(0, 4, 6, 9, 15, 18, 22),
end_hour = c(4, 6, 9, 15, 18, 22, 24)
)
# CREATE BAR CHART OF TRIPS BY SERVICE TYPES
# Ensure 'calendar' has 'day_type' defined
calendar <- calendar %>%
mutate(
day_type = case_when(
monday == 1 & tuesday == 1 & wednesday == 1 & thursday == 1 & friday == 1 & saturday == 0 & sunday == 0 ~ "Weekday",
(saturday == 1 | sunday == 1) ~ "Weekend",
TRUE ~ "irregular"
)
)
# Calendar_dates includes 'service_id', 'date', and 'exception_type'
# Ensure 'calendar_dates' has 'day_type' defined based on the date
calendar_dates <- calendar_dates %>%
filter(exception_type == 1) %>% #we only need exception_type == 1, those added to the service
mutate(
date = ymd(date), # Convert date format if necessary
day_of_week = wday(date, label = TRUE, week_start = 1),
day_type = case_when(
day_of_week %in% c("Mon", "Tue", "Wed", "Thu", "Fri") ~ "Weekday", # Monday to Friday
TRUE ~ "Weekend" # Saturday and Sunday
)
)
# Join calendar_dates with calendar
calendar_joined <- calendar_dates %>%
full_join(calendar, by = "service_id", suffix = c(".dates", ".cal")) %>%
mutate(
final_day_type = case_when(
# Use calendar_dates' day_type if exception_type indicates added service
exception_type == 1 & day_type.dates == "Weekday" ~ "Weekday",
exception_type == 1 & day_type.dates == "Weekend" ~ "Weekend",
# Default to calendar's day_type where there's no specific exception noted
TRUE ~ day_type.cal
)
) %>%
select(service_id, final_day_type) %>%
group_by(service_id, final_day_type) %>%
summarise(count = n(), .groups = 'drop') %>%
arrange(service_id, desc(count)) %>%
distinct(service_id, .keep_all = TRUE) %>% # Make sure no double day types for service ID
select(-count)
dat <- dat %>%
left_join(calendar_joined, by = "service_id")
dat_service_type <- dat %>%
filter(mode %in% c("Bus", "Trolleybus", "Trolley")) %>%
group_by(final_day_type)%>%
summarise(day_type_count = n(), .groups = 'drop') %>%
mutate(total_count = sum(day_type_count),
peak_share = round(day_type_count/sum(day_type_count)*100, digits = 2)) %>%
arrange(factor(final_day_type, levels = c("Weekday", "Weekend"))) %>%
ungroup()
# Summarize data to get the number of trips per hour
hourly_trips <- dat %>%
filter(mode %in% c("Bus", "Trolleybus", "Trolley")) %>%
group_by(hour, final_day_type) %>%
summarise(number_of_trips = n())
# Define a custom palette for the service periods
service_period_colors <- c(
"Owl" = "#E5E5E5",
"Early Morning" = "#C5E0B4",
"AM Peak" = "#A9D08E",
"Mid Day" = "#FFD966",
"PM Peak" = "#F4B084",
"Evening" = "#D5A6BD",
"Late Night" = "#A4C2F4"
)
# Plot the combined graph
ggplot() +
# Add service period background rectangles
geom_rect(data = service_periods, aes(xmin = start_hour, xmax = end_hour, ymin = 0, ymax = max(hourly_trips$number_of_trips), fill = peak_category), alpha = 0.3) +
# Add the line plot for weekday trips
geom_line(data = hourly_trips %>% filter(final_day_type == "Weekday"), aes(x = hour, y = number_of_trips), color = "#F2AE2E", size = 1) +
geom_point(data = hourly_trips %>% filter(final_day_type == "Weekday"), aes(x = hour, y = number_of_trips), color = "#26A699", size = 2) +
# Add the line plot for weekend trips
geom_line(data = hourly_trips %>% filter(final_day_type == "Weekend"), aes(x = hour, y = number_of_trips), color = "#A6123A", size = 1) +
geom_point(data = hourly_trips %>% filter(final_day_type == "Weekend"), aes(x = hour, y = number_of_trips), color = "#26A699", size = 2) +
# Define scales and labels
scale_x_continuous(breaks = 0:23) +
scale_y_continuous(labels = comma) +
scale_fill_manual(values = service_period_colors) +
scale_color_manual(values = c("Weekday" = "#F2AE2E",
"Weekend" = "#A6123A")) +
labs(title = "Number of Trips by Hour with Service Periods",
x = "Hour of Day",
y = "Number of Trips",
fill = "Service Period",
color = "Day Type") +
theme_minimal() +
theme(legend.position = "bottom",
plot.title = element_text(hjust = 0.5))

While bus services are integral to Philadelphia’s transit system, not
all areas benefit equally. Data specifically pertaining to suburban
regions during weekday AM and PM peak hours reveals a stark disparity:
most suburban bus stations face waiting times ranging from 30 to 60
minutes between buses. This significant wait time not only underscores
the challenges faced by commuters like Midge but also highlights a
broader issue affecting many suburban residents across Southeastern
Pennsylvania, who rely heavily on bus transit.
# SET UP STOPS CATEGORY. This step is crucial as we'll use stops information and join them for each kind of maps
# Convert stops data to sf object
stops <- stops %>%
st_as_sf(coords = c("stop_lon", "stop_lat"), crs = 4326, remove = FALSE)
# Load PA and SEPTA region map for base map
PA <- st_read("data/PaCounty2024_05.geojson")
SEPTA_region_map <- PA[PA$COUNTY_NAM %in% c("MONTGOMERY", "BUCKS", "PHILADELPHIA", "DELAWARE", "CHESTER"), ] %>%
select(PA_CTY_COD, COUNTY_NAM, FIPS_COUNT)
# Load Center City boundary
center_city <- st_read("data/20240708_CenterCity/CenterCity.shp")
# Ensure all datasets are in the same CRS
SEPTA_region_map <- st_transform(SEPTA_region_map, crs = st_crs(stops))
center_city <- st_transform(center_city, crs = st_crs(stops))
# Perform the spatial intersection for county
stops <- stops %>%
st_join(SEPTA_region_map, left = TRUE, join = st_intersects) %>%
rename(county = COUNTY_NAM) %>%
mutate(center_city = st_within(stops, center_city, sparse = FALSE))%>%
mutate(center_city = rowSums(center_city) > 0) # Convert logical matrix to vector
# Categorize the stops
stops <- stops %>%
mutate(category = case_when(
county == "PHILADELPHIA" & center_city == TRUE ~ "PHILADELPHIA-Center_City",
county == "PHILADELPHIA" & center_city == FALSE ~ "PHILADELPHIA-Not_Center_City",
TRUE ~ county
)) %>%
select(-"center_city")
# SET UP HOURLY AVERAGE TRIP DATA FOR EACH BUS STOP
dat_trip_count_hr <- dat %>%
filter(mode %in% c("Bus", "Trolleybus", "Trolley")) %>%
group_by(stop_id, mode, final_day_type) %>%
summarise(trip_count = n(),
All_Routes = paste(unique(route_id), collapse = ", "),
.groups = 'drop') %>%
mutate(avg_trip_hr = round(trip_count/24)) %>%
mutate(log_avg_trip_hr = round(log1p(avg_trip_hr), digits = 0)) %>%
mutate(max_freq = ifelse(avg_trip_hr >= 12, "5 MAX",
ifelse (avg_trip_hr >= 6, "10 MAX",
ifelse (avg_trip_hr >= 4, "15 MAX",
ifelse(avg_trip_hr >= 2, "30 MAX", "60 MAX")))))
dat_trip_count_hr <- left_join(dat_trip_count_hr, stops, by = "stop_id")
dat_trip_count_hr <- st_as_sf(dat_trip_count_hr, coords = c("stop_lon", "stop_lat"), crs = 4326, remove = FALSE)
# sum(dat_trip_count_hr$trip_count) #1980505 <- Always make sure you retain the number of observation
# SET UP AVERAGE TRIP DATA PER SERVICE PERIOD FOR EACH BUS STOP
dat_trip_count_prd <- dat %>%
filter(mode %in% c("Bus", "Trolleybus", "Trolley")) %>%
group_by(stop_id, peak_category, mode, final_day_type) %>%
summarise(trip_count = n(),
All_Routes = paste(unique(route_id), collapse = ", "),
.groups = 'drop') %>%
mutate(
hours = durations[peak_category],
avg_trip_hr = round(trip_count / hours)
) %>%
select(-hours) %>%
mutate(log_avg_trip_hr = round(log1p(avg_trip_hr), digits = 0)) %>%
mutate(max_freq = ifelse(avg_trip_hr >= 12, "5 MAX",
ifelse (avg_trip_hr >= 6, "10 MAX",
ifelse (avg_trip_hr >= 4, "15 MAX",
ifelse(avg_trip_hr >= 2, "30 MAX", "60 MAX")))))
dat_trip_count_prd <- left_join(dat_trip_count_prd, stops, by = "stop_id")
dat_trip_count_prd <- st_as_sf(dat_trip_count_prd, coords = c("stop_lon", "stop_lat"), crs = 4326, remove = FALSE)
# sum(dat_trip_count_prd$trip_count) #1980505 <- Always make sure you retain the number of observation
SEPTA_region_map <- st_transform(SEPTA_region_map, st_crs(dat_trip_count_hr))
# # Define color palette
# pal <- colorNumeric(palette = viridisLite::viridis(256, option = "C"), domain = dat_trip_count_hr$avg_trip_hr)
# Define the color codes for bus revolution and reverse them
bus_revolution_colors <- rev(c("#A6123A", "#655BA6", "#26A699", "#F2AE2E", "#F2E5D5"))
# Ensure max_freq is a factor with levels matching the colors
dat_trip_count_hr$max_freq <- factor(dat_trip_count_hr$max_freq, levels = c("5 MAX", "10 MAX", "15 MAX", "30 MAX", "60 MAX"))
# Define the color palette for max_freq
max_freq_colors <- c("5 MAX" = "#4B0082", "10 MAX" = "#FF4500", "15 MAX" = "#A6123A", "30 MAX" = "#26A699", "60 MAX" = "#F2AE2E")
pal_max_freq <- colorFactor(palette = max_freq_colors, domain = dat_trip_count_hr$max_freq)
# # Generate the list for overlayGroups with the desired order
# overlay_groups <- expand.grid(final_day_type = c("Weekday", "Weekend"), peak_category = peak_order) %>%
# mutate(group_name = paste(final_day_type, peak_category, sep = "_")) %>%
# pull(group_name)
# Create the leaflet map
m_avg_trip_hr <- leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addPolygons(data = SEPTA_region_map, color = "black", weight = 1, fill = FALSE) %>%
addCircleMarkers(data = dat_trip_count_hr %>% filter(final_day_type == "Weekday"),
group = "Weekday",
lng = ~stop_lon, lat = ~stop_lat,
color = ~pal_max_freq(max_freq),
radius = 1,
opacity = 0.8,
popup = ~paste0("<b>Stop ID:</b> ", stop_id, "<br>",
"<b>Stop Name:</b> ", stop_name, "<br>",
"<b>Avg Buses/Hr:</b> ", avg_trip_hr, "<br>",
"<b>Max Frequency:</b> ", max_freq, "<br>",
"<b>All Routes:</b> ", All_Routes, "<br>",
"<b>Day Type:</b> ", final_day_type)) %>%
addCircleMarkers(data = dat_trip_count_hr %>% filter(final_day_type == "Weekend"),
group = "Weekend",
lng = ~stop_lon, lat = ~stop_lat,
color = ~pal_max_freq(max_freq),
radius = 1,
opacity = 0.8,
popup = ~paste0("<b>Stop ID:</b> ", stop_id, "<br>",
"<b>Stop Name:</b> ", stop_name, "<br>",
"<b>Avg Buses/Hr:</b> ", avg_trip_hr, "<br>",
"<b>Max Frequency:</b> ", max_freq, "<br>",
"<b>All Routes:</b> ", All_Routes, "<br>",
"<b>Day Type:</b> ", final_day_type)) %>%
addLayersControl(
overlayGroups = c("Weekday", "Weekend"),
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(c("Weekend")) %>%
addLegend(pal = pal_max_freq, values = dat_trip_count_hr$max_freq, title = "Max Frequency",
position = "bottomright")
m_avg_trip_hr
Further analysis of bus stop data by county—excluding Philadelphia’s
Center City—reveals a misleading equality in service distribution across
the counties. While the top bus stops in each county boast a 5-minute
maximum headway, reflecting seemingly equal service levels, the actual
number of buses tells a different story. For instance, even at their
busiest, bus stops in Bucks County see an average of only 15 buses per
hour at the 5 MAX category, starkly less than the 30 buses seen in
similar categories in Philadelphia’s outlying areas. This discrepancy
highlights the concentration of better services still within closer
proximity to urban centers, despite attempts at equitable
distribution.
# Filter and select the top 10 stops for 'mode' == 'bus' on the weekday
top_stops_hr_weekday <- dat_trip_count_hr %>%
filter(mode %in% c("Bus","Trolleybus"), final_day_type == "Weekday") %>%
select(stop_id, stop_name, avg_trip_hr, All_Routes) %>%
arrange(desc(avg_trip_hr)) %>%
slice_head(n = 10)
# Create a table with kable and enhance it with kableExtra
kable(top_stops_hr_weekday, "html", booktabs = TRUE,
caption = "Top 10 Bus Stops by Average Trips Per Hour on the Weekday",
align = c('l', rep('l', ncol(top_stops_hr_weekday) - 1))) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
column_spec(1, bold = TRUE, color = "#655BA6", width = "5em") %>%
column_spec(3, bold = TRUE, color = "#A6123A", extra_css = "text-align: right;")
Top 10 Bus Stops by Average Trips Per Hour on the Weekday
|
stop_id
|
stop_name
|
avg_trip_hr
|
All_Routes
|
geometry
|
|
21204
|
Frankford Transportation Center - Main Dropoff
|
47
|
14, 19, 20, 24, 26, 3, 50, 58, 67, 88, BLVDDIR, MFO
|
POINT (-75.07748 40.02329)
|
|
1148
|
69th St Transportation Center South Terminal
|
44
|
103, 104, 105, 107, 108, 109, 111, 113, 110, 120, 21, 30, 65, 68, MFO
|
POINT (-75.25828 39.96208)
|
|
10266
|
Market St & 15th St
|
40
|
124, 125, 17, 31, 32, 33, 38, 44, 48, 62, BSO, MFO
|
POINT (-75.16548 39.95255)
|
|
10272
|
Market St & 18th St
|
36
|
124, 125, 17, 31, 32, 33, 38, 44, 48, 62
|
POINT (-75.17017 39.95312)
|
|
18451
|
Market St & 16th St
|
36
|
124, 125, 17, 31, 32, 33, 38, 44, 48, 62
|
POINT (-75.16705 39.95274)
|
|
136
|
Cheltenham Av & Ogontz Av Loop
|
34
|
16, 6, H, XH
|
POINT (-75.1591 40.07483)
|
|
17842
|
JFK Blvd & 15th St
|
34
|
MANNLP, 124, 125, 17, 27, 31, 32, 33, 38, 44, 446, 62, 78
|
POINT (-75.16476 39.95365)
|
|
8936
|
JFK Blvd & 17th St
|
31
|
MANNLP, 124, 125, 17, 31, 32, 33, 38, 44, 446, 62
|
POINT (-75.16806 39.95406)
|
|
101
|
Pier 70 WalMart & Home Depot
|
29
|
25, 29, 64, 7
|
POINT (-75.14167 39.92531)
|
|
15497
|
69th St Transit Center
|
29
|
104, 109, 110, 111, 112, 120, 123, 126
|
POINT (-75.25968 39.96217)
|
# Define a custom palette for the categories
category_colors <- c(
"BUCKS" = "#A9D08E",
"CHESTER" = "#FFD966",
"DELAWARE" = "#F4B084",
"MONTGOMERY" = "#D5A6BD",
"PHILADELPHIA-Not_Center_City" = "#A4C2F4"
)
# Example with Weekday data
dat_filtered_weekday <- dat_trip_count_hr %>%
filter(mode %in% c("Bus","Trolleybus"), final_day_type == "Weekday" & category != "PHILADELPHIA-Center_City")
# Function to get top 5 stops per category
get_top_stops_per_category <- function(data, n = 5) {
data %>%
group_by(category) %>%
select(stop_id, stop_name, avg_trip_hr, max_freq, All_Routes, category) %>%
arrange(desc(avg_trip_hr)) %>%
slice_head(n = n) %>%
ungroup()
}
# Get top 5 stops per category for Weekday
top_stops_hr_weekday <- get_top_stops_per_category(dat_filtered_weekday, n = 5)
# Create the table for Weekday with row colors
kable(top_stops_hr_weekday, "html", booktabs = TRUE,
caption = "Top 5 Bus Stops by Average Trips Per Hour on the Weekday",
align = c('l', rep('l', ncol(top_stops_hr_weekday) - 1))) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
column_spec(1, bold = TRUE, color = "#655BA6", width = "5em") %>%
column_spec(3, bold = TRUE, color = "#A6123A", extra_css = "text-align: right;")
Top 5 Bus Stops by Average Trips Per Hour on the Weekday
|
stop_id
|
stop_name
|
avg_trip_hr
|
max_freq
|
All_Routes
|
category
|
geometry
|
|
330
|
Neshaminy Mall
|
16
|
5 MAX
|
128, 130, 14, 58, BLVDDIR
|
BUCKS
|
POINT (-74.95357 40.13806)
|
|
23080
|
Rockhill Rd & Neshaminy Rd - MBFS
|
14
|
5 MAX
|
128, 130, 14, 58, BLVDDIR
|
BUCKS
|
POINT (-74.95696 40.13665)
|
|
23071
|
Rockhill Rd & Neshaminy Blvd - MBNS
|
13
|
5 MAX
|
14, 58, BLVDDIR
|
BUCKS
|
POINT (-74.95725 40.13658)
|
|
22762
|
Roosevelt Blvd & Old Lincoln Hwy - MBFS
|
12
|
5 MAX
|
1, 14, BLVDDIR
|
BUCKS
|
POINT (-74.97913 40.12061)
|
|
25881
|
Roosevelt Blvd & Old Lincoln Hwy - MBNS
|
12
|
5 MAX
|
1, 14, BLVDDIR
|
BUCKS
|
POINT (-74.97909 40.12022)
|
|
1990
|
West Chester Transportation Center
|
8
|
10 MAX
|
135, 92, 104
|
CHESTER
|
POINT (-75.6073 39.9579)
|
|
18729
|
Chestnut St & Church St
|
6
|
10 MAX
|
135, 92, 104
|
CHESTER
|
POINT (-75.60707 39.96102)
|
|
18730
|
Chestnut St & Darlington St
|
6
|
10 MAX
|
135, 92, 104
|
CHESTER
|
POINT (-75.60838 39.96047)
|
|
30146
|
New St & Chestnut St - FS
|
6
|
10 MAX
|
135, 92, 104
|
CHESTER
|
POINT (-75.60984 39.95955)
|
|
14820
|
Church St & University Av - FS
|
5
|
15 MAX
|
104
|
CHESTER
|
POINT (-75.6 39.95252)
|
|
1148
|
69th St Transportation Center South Terminal
|
44
|
5 MAX
|
103, 104, 105, 107, 108, 109, 111, 113, 110, 120, 21, 30, 65, 68, MFO
|
DELAWARE
|
POINT (-75.25828 39.96208)
|
|
15497
|
69th St Transit Center
|
29
|
5 MAX
|
104, 109, 110, 111, 112, 120, 123, 126
|
DELAWARE
|
POINT (-75.25968 39.96217)
|
|
2024
|
69th St Transportation Center North Terminal
|
25
|
5 MAX
|
103, 105, 106, 123, 30, 65
|
DELAWARE
|
POINT (-75.25861 39.96282)
|
|
14785
|
Chester Transportation Center
|
20
|
5 MAX
|
117, 119, 113, 37
|
DELAWARE
|
POINT (-75.35997 39.84921)
|
|
30597
|
Darby Transportation Center - bus
|
17
|
5 MAX
|
114, 115, 113
|
DELAWARE
|
POINT (-75.26358 39.9191)
|
|
136
|
Cheltenham Av & Ogontz Av Loop
|
34
|
5 MAX
|
16, 6, H, XH
|
MONTGOMERY
|
POINT (-75.1591 40.07483)
|
|
1649
|
Norristown Transportation Center
|
22
|
5 MAX
|
131, 90, 93, 96, 97, 98, 99
|
MONTGOMERY
|
POINT (-75.34482 40.11345)
|
|
809
|
Willow Grove Park Mall
|
20
|
5 MAX
|
310, 311, 95, 22, 55
|
MONTGOMERY
|
POINT (-75.12249 40.14226)
|
|
1827
|
Plaza at King of Prussia
|
19
|
5 MAX
|
124, 139, 92, 99, 123, 125
|
MONTGOMERY
|
POINT (-75.39311 40.08983)
|
|
246
|
Plymouth Meeting Mall
|
16
|
5 MAX
|
150, 90, 95, 98, 27, L
|
MONTGOMERY
|
POINT (-75.28403 40.1172)
|
|
21204
|
Frankford Transportation Center - Main Dropoff
|
47
|
5 MAX
|
14, 19, 20, 24, 26, 3, 50, 58, 67, 88, BLVDDIR, MFO
|
PHILADELPHIA-Not_Center_City
|
POINT (-75.07748 40.02329)
|
|
101
|
Pier 70 WalMart & Home Depot
|
29
|
5 MAX
|
25, 29, 64, 7
|
PHILADELPHIA-Not_Center_City
|
POINT (-75.14167 39.92531)
|
|
28
|
Wissahickon Transportation Center - onsite
|
28
|
5 MAX
|
124, 125, 1, 35, 38, 61, R
|
PHILADELPHIA-Not_Center_City
|
POINT (-75.20724 40.0149)
|
|
8712
|
Hunting Park Av & 22nd St
|
27
|
5 MAX
|
33, 445, 447, 56, H, R, XH
|
PHILADELPHIA-Not_Center_City
|
POINT (-75.16457 40.01089)
|
|
410
|
Philadelphia Mills & Marshalls
|
24
|
5 MAX
|
129, 130, 20, 50, 67, 84
|
PHILADELPHIA-Not_Center_City
|
POINT (-74.96447 40.08583)
|
LS0tDQp0aXRsZTogIldhaXRpbmcgZm9yIENoYW5nZTogVGhlIFVyZ2VudCBOZWVkIGZvciBFcXVpdGFibGUgQnVzIFNlcnZpY2VzIGluIFN1YnVyYmFuIFBoaWxhZGVscGhpYSINCmF1dGhvcjogIlJlZ3kgU2VwdGlhbiINCmRhdGU6ICIyMDI0LTA5LTI0Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY2FjaGUgPSBGQUxTRSkNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5dHJhbnNpdCkNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShobXMpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHdrKQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkodmlyaWRpc0xpdGUpDQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQ0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KGxlYWZsZXQuZXh0cmFzKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeSh3ZWJzaG90MikNCmxpYnJhcnkobWFnaWNrKQ0KDQojIERlZmluZSB0aGUgY29sb3IgY29kZXMNCmJ1c19yZXZvbHV0aW9uX2NvbG9ycyA8LSBjKCIjQTYxMjNBIiwgIiM2NTVCQTYiLCAiIzI2QTY5OSIsICIjRjJBRTJFIiwgIiNGMkU1RDUiKQ0KDQojIENyZWF0ZSBhIGN1c3RvbSBwYWxldHRlIGZ1bmN0aW9uDQpidXNfcmV2b2x1dGlvbl9wYWxldHRlIDwtIGZ1bmN0aW9uKG4pIHsNCiAgY29sb3JSYW1wUGFsZXR0ZShidXNfcmV2b2x1dGlvbl9jb2xvcnMpKG4pDQp9DQoNCndvcmtkaXIgPC0gIn4vR2l0SHViL01VU0EtNjMxMF9Bc3NpZ25tZW50LTIiICMgQ2hhbmdlIHRoZSBkaXJlY3RvcnkNCnNldHdkKHdvcmtkaXIpDQpgYGANCiMjIyBBIENvbW11dGVyJ3MgQ2hhbGxlbmdlIGluIFN1YnVyYmFuIFBoaWxhZGVscGhpYSAgDQoNCkV2ZXJ5IG1vcm5pbmcsIE1pZGdlLCBhIHJlY2VudCBncmFkdWF0ZSB3b3JraW5nIGluIGRvd250b3duIFBoaWxhZGVscGhpYSwgc3RhcnRzIGhlciBkYXkgYmF0dGxpbmcgdGhlIGxpbWl0YXRpb25zIG9mIHN1YnVyYmFuIGJ1cyB0cmFuc2l0LiBXaXRoIHNlcnZpY2VzIGZhciBsZXNzIGZyZXF1ZW50IHRoYW4gdGhvc2UgaW4gdXJiYW4gY2VudGVycywgaGVyIHN0b3J5IGlsbHVzdHJhdGVzIHRoZSBicm9hZGVyIHN0cnVnZ2xlcyBmYWNlZCBieSBtYW55IHN1YnVyYmFuIGNvbW11dGVycywgZGVwZW5kZW50IG9uIGEgc3lzdGVtIHRoYXQgZG9lc24ndCBtZWV0IHRoZWlyIG5lZWRzLiAgDQoNCiMjIyBVcmJhbiBFZmZpY2llbmN5IHZzLiBTdWJ1cmJhbiBTY2FyY2l0eSAgDQoNClVzaW5nIHRoZSBsYXRlc3QgU0VQVEEgR2VuZXJhbCBUcmFuc2l0IEZlZWQgU3BlY2lmaWNhdGlvbiAoR1RGUykgZGF0YVteMV0sIGl0IHJldmVhbHMgdGhhdCBhIHN0YWdnZXJpbmcgODAlIG9mIFNFUFRBJ3MgdHJpcHMgYXJlIG1hZGUgYnkgYnVzLCBzaG93Y2FzaW5nIHRoZSBjcml0aWNhbCByb2xlIG9mIGJ1cyB0cmFuc2l0IGluIFBoaWxhZGVscGhpYeKAmXMgdHJhbnNwb3J0YXRpb24gbmV0d29yay4gVGhpcyBzdGF0aXN0aWMgdW5kZXJsaW5lcyB0aGUgaGVhdnkgcmVsaWFuY2Ugb24gYnVzZXMgYWNyb3NzIHRoZSBjaXR5LCBub3QganVzdCBpbiBzdWJ1cmJhbiBhcmVhcy4gDQoNClteMV06IFtTRVBUQSBHVEZTXShodHRwczovL3d3dzMuc2VwdGEub3JnL2RldmVsb3Blci9ndGZzX3B1YmxpYy56aXApDQoNCg0KYGBge3IgaW1wb3J0IGd0ZnMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIHJlc3VsdHMgPSAnaGlkZSd9DQojIEltcG9ydCBHVEZTIGRhdGENCnN0b3BzIDwtIHJlYWQuY3N2KCJkYXRhL2dvb2dsZV9idXMvc3RvcHMudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQ0Kc3RvcF90aW1lcyA8LSByZWFkLmNzdigiZGF0YS9nb29nbGVfYnVzL3N0b3BfdGltZXMudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQ0KdHJpcHMgPC0gcmVhZC5jc3YoImRhdGEvZ29vZ2xlX2J1cy90cmlwcy50eHQiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIpDQpzaGFwZXMgPC0gcmVhZC5jc3YoImRhdGEvZ29vZ2xlX2J1cy9zaGFwZXMudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQ0Kcm91dGVzIDwtIHJlYWQuY3N2KCJkYXRhL2dvb2dsZV9idXMvcm91dGVzLnR4dCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIikNCmNhbGVuZGFyIDwtIHJlYWQuY3N2KCJkYXRhL2dvb2dsZV9idXMvY2FsZW5kYXIudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQ0KY2FsZW5kYXJfZGF0ZXMgPC0gcmVhZC5jc3YoImRhdGEvZ29vZ2xlX2J1cy9jYWxlbmRhcl9kYXRlcy50eHQiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIpDQoNCiMgSW1wb3J0IFN3aWZ0bHkgZGF0YQ0Kc3dpZnRseSA8LSBzdF9yZWFkKCJkYXRhL1N3aWZ0bHlTaGFwZXMvZ2VvSlNPTnMvU0VQVEFTdXJmYWNlX0hpZ2hSZXNTcGVlZF92Mi5qc29uIikNCg0KYGBgDQoNCmBgYHtyIHRvdGFsIHRyaXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9DQojIEpvaW4gc3RvcF90aW1lcyBhbmQgdHJpcHMsIGZyb20gbm93IG9uIGRhdCB3aWxsIGJlIG91ciBtYWluIGRhdGEgc2V0DQpkYXQgPC0gbGVmdF9qb2luKHN0b3BfdGltZXMsIHRyaXBzLCBieSA9ICJ0cmlwX2lkIikNCg0KIyBqb2luIGRhdCBhbmQgcm91dGVzDQpkYXQgPC0gbGVmdF9qb2luKGRhdCwgcm91dGVzLCBieSA9ICJyb3V0ZV9pZCIpDQoNCiNkZXRlcm1pbmUgcm91dGVfdHlwZQ0KZGF0IDwtIGRhdCAlPiUNCiAgbXV0YXRlKG1vZGUgPSBpZl9lbHNlKHJvdXRlX3R5cGUgPT0gMCwgIlRyb2xsZXkiLCBpZmVsc2Uocm91dGVfdHlwZSA9PSAxLCAiTWV0cm8iLCBpZmVsc2Uocm91dGVfdHlwZSA9PSAzLCAiQnVzIiwgIlRyb2xsZXlidXMiKSkpKQ0KDQojIE1vZGUgc2hhcmUgKG5vdCByaWRlcnNoaXApDQpkYXRfdHJpcF9ieV9tb2RlIDwtIGRhdCAlPiUNCiAgZ3JvdXBfYnkobW9kZSkgJT4lDQogIHN1bW1hcmlzZShtb2RlX2NvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgbXV0YXRlKHRvdGFsX2NvdW50ID0gc3VtKG1vZGVfY291bnQpLA0KICAgICAgICAgbW9kZV9zaGFyZSA9IHJvdW5kKG1vZGVfY291bnQvc3VtKG1vZGVfY291bnQpKjEwMCwgZGlnaXRzID0gMikpICU+JQ0KICB1bmdyb3VwKCkgDQogIA0KIyBQbG90IG1vZGUgc2hhcmUgdXNpbmcgZ2dwbG90MiBhbmQgdGhlIGN1c3RvbSBwYWxldHRlDQpnZ3Bsb3QoZGF0X3RyaXBfYnlfbW9kZSwgYWVzKHggPSBtb2RlLCB5ID0gbW9kZV9zaGFyZSwgZmlsbCA9IG1vZGUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNzUpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYnVzX3Jldm9sdXRpb25fcGFsZXR0ZShsZW5ndGgodW5pcXVlKGRhdF90cmlwX2J5X21vZGUkbW9kZSkpKSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKG1vZGVfc2hhcmUsICIlICgiLCBtb2RlX2NvdW50LCAiIHRyaXBzKSIpKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuNSkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGUgU2hhcmUgYnkgVHJhbnNpdCBUeXBlIiwNCiAgICAgICB4ID0gIk1vZGUiLA0KICAgICAgIHkgPSAiTW9kZSBTaGFyZSAoJSkiLA0KICAgICAgIGZpbGwgPSAiTW9kZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgdG90YWwgdHJpcCBidXMgJiB0cm9sbGV5IGJ1cyAxLjk4MC41MDUsIHRvdGFsIG9ic2VydmF0aW9ucyAyLjAyNi42NzcNCmBgYA0KDQpUaGUgZGF0YSBhbHNvIGhpZ2hsaWdodHMgZGlzdGluY3QgcGF0dGVybnMgaW4gcmlkZXJzaGlwLCB3aXRoIGJ1cyB1c2FnZSBwZWFraW5nIGR1cmluZyBtb3JuaW5nICg2LTlBTSkgYW5kIGV2ZW5pbmcgKDMtNlBNKSBydXNoIGhvdXJzIG9uIHdlZWtkYXlzLiBUaGlzIGNvbnRyYXN0cyB3aXRoIHdlZWtlbmRzLCB3aGVyZSB0aGUgcGF0dGVybiBmbGF0dGVucywgaW5kaWNhdGluZyBhIG1vcmUgZXZlbiBkaXN0cmlidXRpb24gb2YgdHJpcHMgdGhyb3VnaG91dCB0aGUgZGF5Lg0KICANCmBgYHtyIHBlYWsgdGltZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgcmVzdWx0cyA9ICdoaWRlJ30NCiMgQ1JFQVRFIExJTkUgR1JBUEggT0YgVFJJUFMgQlkgU0VSVklDRSBQRVJJT0RTDQoNCiMgRnVuY3Rpb24gdG8gbm9ybWFsaXplIHRpbWVzIGdyZWF0ZXIgdGhhbiAyNDowMDowMC4gSW1wb3J0YW50IHN0ZXA6IGNoZWNrIHRoZSBzdG9wX3RpbWVzJGFycml2YWxfdGltZSBhbmQgZGVwYXJ0dXJlX3RpbWUgdmFsdWVzIGlmIGl0J3MgZ3JlYXRlciB0aGFuIDI0OjAwIA0Kbm9ybWFsaXplX3RpbWUgPC0gZnVuY3Rpb24odGltZV9zdHIpIHsNCiAgdGltZV9wYXJ0cyA8LSBzdHJzcGxpdCh0aW1lX3N0ciwgIjoiKVtbMV1dDQogIGhvdXJzIDwtIGFzLm51bWVyaWModGltZV9wYXJ0c1sxXSkNCiAgbWludXRlcyA8LSBhcy5udW1lcmljKHRpbWVfcGFydHNbMl0pDQogIHNlY29uZHMgPC0gYXMubnVtZXJpYyh0aW1lX3BhcnRzWzNdKQ0KICANCiAgaWYgKGhvdXJzID49IDI0KSB7DQogICAgaG91cnMgPC0gaG91cnMgLSAyNA0KICAgIHRpbWVfc3RyIDwtIHNwcmludGYoIiUwMmQ6JTAyZDolMDJkIiwgaG91cnMsIG1pbnV0ZXMsIHNlY29uZHMpDQogIH0NCiAgDQogIHJldHVybih0aW1lX3N0cikNCn0NCg0KI2lkZW50aWZ5IHBlYWtfcG0gY2F0ZWdvcnkgDQpkYXQgPC0gZGF0ICU+JQ0KICBtdXRhdGUoZGVwYXJ0dXJlX3RpbWUgPSBzYXBwbHkoZGVwYXJ0dXJlX3RpbWUsIG5vcm1hbGl6ZV90aW1lKSwgIyBkZXBhcnR1cmVfdGltZSdzIG5vcm1hbGl6ZWQNCiAgICAgICAgIGRlcGFydHVyZV90aW1lID0gbHVicmlkYXRlOjpobXMoZGVwYXJ0dXJlX3RpbWUpLA0KICAgICAgICAgaG91ciA9IGhvdXIoZGVwYXJ0dXJlX3RpbWUpLCAgIyBFeHRyYWN0IGhvdXIgY29tcG9uZW50IGZvciBlYXNpZXIgY29uZGl0aW9uIGNoZWNraW5nDQogICAgICAgICBwZWFrX2NhdGVnb3J5ID0gaWZfZWxzZShob3VyID49IDQgJiBob3VyIDwgNiwgIkVhcmx5IE1vcm5pbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShob3VyID49IDYgJiBob3VyIDwgOSwgIkFNIFBlYWsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKGhvdXIgPj0gOSAmIGhvdXIgPCAxNSwgIk1pZCBEYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoaG91ciA+PSAxNSAmIGhvdXIgPCAxOCwgIlBNIFBlYWsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShob3VyID49IDE4ICYgaG91ciA8IDIyLCAiRXZlbmluZyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaG91ciA+PSAyMiAmIGhvdXIgPCAyNCwgIkxhdGUgTmlnaHQiLCAiT3dsIikpKSkpKSkgDQoNCiMgIyBDaGVjayBpZiB0aGVyZSBhcmUgYW55IE5BIHZhbHVlcyBpbiB0aGUgJ2RlcGFydHVyZV90aW1lJyBjb2x1bW4NCiMgc3VtbWFyaXNlKGRhdCwNCiMgICAgIG5hX2NvdW50ID0gc3VtKGlzLm5hKGRlcGFydHVyZV90aW1lKSksICNDb3VudCBOQSB2YWx1ZXMNCiMgICAgIHRvdGFsX3Jvd3MgPSBuKCksICAjIFRvdGFsIG51bWJlciBvZiByb3dzIGZvciBjb250ZXh0DQojICAgICBwZXJjZW50YWdlX25hID0gbmFfY291bnQgLyB0b3RhbF9yb3dzICogMTAwICAjIFBlcmNlbnRhZ2Ugb2YgTkEgdmFsdWVzDQojICAgKQ0KDQpkdXJhdGlvbnMgPC0gYygiT3dsIiA9IDQsICJFYXJseSBNb3JuaW5nIiA9IDIsICJBTSBQZWFrIiA9IDMsICJNaWQgRGF5IiA9IDYsICJQTSBQZWFrIiA9IDMsICJFdmVuaW5nIiA9IDQsICJMYXRlIE5pZ2h0IiA9IDIpDQoNCmRhdF9zZXJ2aWNlX3BlcmlvZCA8LSBkYXQgJT4lDQogIGZpbHRlcihtb2RlICVpbiUgYygiQnVzIiwgIlRyb2xsZXlidXMiLCAiVHJvbGxleSIpKSAlPiUNCiAgZ3JvdXBfYnkocGVha19jYXRlZ29yeSklPiUNCiAgc3VtbWFyaXNlKHBlYWtfY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQ0KICBtdXRhdGUodG90YWxfY291bnQgPSBzdW0ocGVha19jb3VudCksDQogICAgICAgICBwZWFrX3NoYXJlID0gcm91bmQocGVha19jb3VudC9zdW0ocGVha19jb3VudCkqMTAwLCBkaWdpdHMgPSAyKSkgJT4lDQogIGFycmFuZ2UoZmFjdG9yKHBlYWtfY2F0ZWdvcnksIGxldmVscyA9IGMoIk93bCIsICJFYXJseSBNb3JuaW5nIiwgIkFNIFBlYWsiLCAiTWlkIERheSIsICJQTSBQZWFrIiwgIkV2ZW5pbmciLCAiTGF0ZSBOaWdodCIpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHNlcnZpY2UgcGVyaW9kcw0Kc2VydmljZV9wZXJpb2RzIDwtIGRhdGEuZnJhbWUoDQogIHBlYWtfY2F0ZWdvcnkgPSBjKCJPd2wiLCAiRWFybHkgTW9ybmluZyIsICJBTSBQZWFrIiwgIk1pZCBEYXkiLCAiUE0gUGVhayIsICJFdmVuaW5nIiwgIkxhdGUgTmlnaHQiKSwNCiAgc3RhcnRfaG91ciA9IGMoMCwgNCwgNiwgOSwgMTUsIDE4LCAyMiksDQogIGVuZF9ob3VyID0gYyg0LCA2LCA5LCAxNSwgMTgsIDIyLCAyNCkNCikNCg0KIyBDUkVBVEUgQkFSIENIQVJUIE9GIFRSSVBTIEJZIFNFUlZJQ0UgVFlQRVMNCg0KIyBFbnN1cmUgJ2NhbGVuZGFyJyBoYXMgJ2RheV90eXBlJyBkZWZpbmVkDQpjYWxlbmRhciA8LSBjYWxlbmRhciAlPiUNCiAgbXV0YXRlKA0KICAgIGRheV90eXBlID0gY2FzZV93aGVuKA0KICAgICAgbW9uZGF5ID09IDEgJiB0dWVzZGF5ID09IDEgJiB3ZWRuZXNkYXkgPT0gMSAmIHRodXJzZGF5ID09IDEgJiBmcmlkYXkgPT0gMSAmIHNhdHVyZGF5ID09IDAgJiBzdW5kYXkgPT0gMCB+ICJXZWVrZGF5IiwNCiAgICAgIChzYXR1cmRheSA9PSAxIHwgc3VuZGF5ID09IDEpIH4gIldlZWtlbmQiLA0KICAgICAgVFJVRSB+ICJpcnJlZ3VsYXIiDQogICAgKQ0KICApDQoNCiMgQ2FsZW5kYXJfZGF0ZXMgaW5jbHVkZXMgJ3NlcnZpY2VfaWQnLCAnZGF0ZScsIGFuZCAnZXhjZXB0aW9uX3R5cGUnDQojIEVuc3VyZSAnY2FsZW5kYXJfZGF0ZXMnIGhhcyAnZGF5X3R5cGUnIGRlZmluZWQgYmFzZWQgb24gdGhlIGRhdGUNCmNhbGVuZGFyX2RhdGVzIDwtIGNhbGVuZGFyX2RhdGVzICU+JQ0KICBmaWx0ZXIoZXhjZXB0aW9uX3R5cGUgPT0gMSkgJT4lICN3ZSBvbmx5IG5lZWQgZXhjZXB0aW9uX3R5cGUgPT0gMSwgdGhvc2UgYWRkZWQgdG8gdGhlIHNlcnZpY2UNCiAgbXV0YXRlKA0KICAgIGRhdGUgPSB5bWQoZGF0ZSksICAjIENvbnZlcnQgZGF0ZSBmb3JtYXQgaWYgbmVjZXNzYXJ5DQogICAgZGF5X29mX3dlZWsgPSB3ZGF5KGRhdGUsIGxhYmVsID0gVFJVRSwgd2Vla19zdGFydCA9IDEpLA0KICAgIGRheV90eXBlID0gY2FzZV93aGVuKA0KICAgICAgZGF5X29mX3dlZWsgJWluJSBjKCJNb24iLCAiVHVlIiwgIldlZCIsICJUaHUiLCAiRnJpIikgfiAiV2Vla2RheSIsICAjIE1vbmRheSB0byBGcmlkYXkNCiAgICAgIFRSVUUgfiAiV2Vla2VuZCIgICMgU2F0dXJkYXkgYW5kIFN1bmRheQ0KICAgICkNCiAgKQ0KDQojIEpvaW4gY2FsZW5kYXJfZGF0ZXMgd2l0aCBjYWxlbmRhcg0KY2FsZW5kYXJfam9pbmVkIDwtIGNhbGVuZGFyX2RhdGVzICU+JQ0KICBmdWxsX2pvaW4oY2FsZW5kYXIsIGJ5ID0gInNlcnZpY2VfaWQiLCBzdWZmaXggPSBjKCIuZGF0ZXMiLCAiLmNhbCIpKSAlPiUNCiAgbXV0YXRlKA0KICAgIGZpbmFsX2RheV90eXBlID0gY2FzZV93aGVuKA0KICAgICAgIyBVc2UgY2FsZW5kYXJfZGF0ZXMnIGRheV90eXBlIGlmIGV4Y2VwdGlvbl90eXBlIGluZGljYXRlcyBhZGRlZCBzZXJ2aWNlDQogICAgICBleGNlcHRpb25fdHlwZSA9PSAxICYgZGF5X3R5cGUuZGF0ZXMgPT0gIldlZWtkYXkiIH4gIldlZWtkYXkiLA0KICAgICAgZXhjZXB0aW9uX3R5cGUgPT0gMSAmIGRheV90eXBlLmRhdGVzID09ICJXZWVrZW5kIiB+ICJXZWVrZW5kIiwNCiAgICAgICMgRGVmYXVsdCB0byBjYWxlbmRhcidzIGRheV90eXBlIHdoZXJlIHRoZXJlJ3Mgbm8gc3BlY2lmaWMgZXhjZXB0aW9uIG5vdGVkDQogICAgICBUUlVFIH4gZGF5X3R5cGUuY2FsDQogICAgKQ0KICApICU+JQ0KICBzZWxlY3Qoc2VydmljZV9pZCwgZmluYWxfZGF5X3R5cGUpICU+JQ0KICBncm91cF9ieShzZXJ2aWNlX2lkLCBmaW5hbF9kYXlfdHlwZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGFycmFuZ2Uoc2VydmljZV9pZCwgZGVzYyhjb3VudCkpICU+JQ0KICBkaXN0aW5jdChzZXJ2aWNlX2lkLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUgIyBNYWtlIHN1cmUgbm8gZG91YmxlIGRheSB0eXBlcyBmb3Igc2VydmljZSBJRA0KICBzZWxlY3QoLWNvdW50KQ0KICANCmRhdCA8LSBkYXQgJT4lDQogIGxlZnRfam9pbihjYWxlbmRhcl9qb2luZWQsIGJ5ID0gInNlcnZpY2VfaWQiKQ0KDQpkYXRfc2VydmljZV90eXBlIDwtIGRhdCAlPiUNCiAgZmlsdGVyKG1vZGUgJWluJSBjKCJCdXMiLCAiVHJvbGxleWJ1cyIsICJUcm9sbGV5IikpICU+JQ0KICBncm91cF9ieShmaW5hbF9kYXlfdHlwZSklPiUNCiAgc3VtbWFyaXNlKGRheV90eXBlX2NvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgbXV0YXRlKHRvdGFsX2NvdW50ID0gc3VtKGRheV90eXBlX2NvdW50KSwNCiAgICAgICAgIHBlYWtfc2hhcmUgPSByb3VuZChkYXlfdHlwZV9jb3VudC9zdW0oZGF5X3R5cGVfY291bnQpKjEwMCwgZGlnaXRzID0gMikpICU+JQ0KICBhcnJhbmdlKGZhY3RvcihmaW5hbF9kYXlfdHlwZSwgbGV2ZWxzID0gYygiV2Vla2RheSIsICJXZWVrZW5kIikpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgU3VtbWFyaXplIGRhdGEgdG8gZ2V0IHRoZSBudW1iZXIgb2YgdHJpcHMgcGVyIGhvdXINCmhvdXJseV90cmlwcyA8LSBkYXQgJT4lDQogIGZpbHRlcihtb2RlICVpbiUgYygiQnVzIiwgIlRyb2xsZXlidXMiLCAiVHJvbGxleSIpKSAlPiUNCiAgZ3JvdXBfYnkoaG91ciwgZmluYWxfZGF5X3R5cGUpICU+JQ0KICBzdW1tYXJpc2UobnVtYmVyX29mX3RyaXBzID0gbigpKQ0KDQojIERlZmluZSBhIGN1c3RvbSBwYWxldHRlIGZvciB0aGUgc2VydmljZSBwZXJpb2RzDQpzZXJ2aWNlX3BlcmlvZF9jb2xvcnMgPC0gYygNCiAgIk93bCIgPSAiI0U1RTVFNSIsDQogICJFYXJseSBNb3JuaW5nIiA9ICIjQzVFMEI0IiwNCiAgIkFNIFBlYWsiID0gIiNBOUQwOEUiLA0KICAiTWlkIERheSIgPSAiI0ZGRDk2NiIsDQogICJQTSBQZWFrIiA9ICIjRjRCMDg0IiwNCiAgIkV2ZW5pbmciID0gIiNENUE2QkQiLA0KICAiTGF0ZSBOaWdodCIgPSAiI0E0QzJGNCINCikNCg0KIyBQbG90IHRoZSBjb21iaW5lZCBncmFwaA0KZ2dwbG90KCkgKw0KICAjIEFkZCBzZXJ2aWNlIHBlcmlvZCBiYWNrZ3JvdW5kIHJlY3RhbmdsZXMNCiAgZ2VvbV9yZWN0KGRhdGEgPSBzZXJ2aWNlX3BlcmlvZHMsIGFlcyh4bWluID0gc3RhcnRfaG91ciwgeG1heCA9IGVuZF9ob3VyLCB5bWluID0gMCwgeW1heCA9IG1heChob3VybHlfdHJpcHMkbnVtYmVyX29mX3RyaXBzKSwgZmlsbCA9IHBlYWtfY2F0ZWdvcnkpLCBhbHBoYSA9IDAuMykgKw0KICAjIEFkZCB0aGUgbGluZSBwbG90IGZvciB3ZWVrZGF5IHRyaXBzDQogIGdlb21fbGluZShkYXRhID0gaG91cmx5X3RyaXBzICU+JSBmaWx0ZXIoZmluYWxfZGF5X3R5cGUgPT0gIldlZWtkYXkiKSwgYWVzKHggPSBob3VyLCB5ID0gbnVtYmVyX29mX3RyaXBzKSwgY29sb3IgPSAiI0YyQUUyRSIsIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGhvdXJseV90cmlwcyAlPiUgZmlsdGVyKGZpbmFsX2RheV90eXBlID09ICJXZWVrZGF5IiksIGFlcyh4ID0gaG91ciwgeSA9IG51bWJlcl9vZl90cmlwcyksIGNvbG9yID0gIiMyNkE2OTkiLCBzaXplID0gMikgKw0KICAjIEFkZCB0aGUgbGluZSBwbG90IGZvciB3ZWVrZW5kIHRyaXBzDQogIGdlb21fbGluZShkYXRhID0gaG91cmx5X3RyaXBzICU+JSBmaWx0ZXIoZmluYWxfZGF5X3R5cGUgPT0gIldlZWtlbmQiKSwgYWVzKHggPSBob3VyLCB5ID0gbnVtYmVyX29mX3RyaXBzKSwgY29sb3IgPSAiI0E2MTIzQSIsIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGhvdXJseV90cmlwcyAlPiUgZmlsdGVyKGZpbmFsX2RheV90eXBlID09ICJXZWVrZW5kIiksIGFlcyh4ID0gaG91ciwgeSA9IG51bWJlcl9vZl90cmlwcyksIGNvbG9yID0gIiMyNkE2OTkiLCBzaXplID0gMikgKw0KICAjIERlZmluZSBzY2FsZXMgYW5kIGxhYmVscw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMDoyMykgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2VydmljZV9wZXJpb2RfY29sb3JzKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJXZWVrZGF5IiA9ICIjRjJBRTJFIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXZWVrZW5kIiA9ICIjQTYxMjNBIikpICsNCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgVHJpcHMgYnkgSG91ciB3aXRoIFNlcnZpY2UgUGVyaW9kcyIsDQogICAgICAgeCA9ICJIb3VyIG9mIERheSIsDQogICAgICAgeSA9ICJOdW1iZXIgb2YgVHJpcHMiLA0KICAgICAgIGZpbGwgPSAiU2VydmljZSBQZXJpb2QiLA0KICAgICAgIGNvbG9yID0gIkRheSBUeXBlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQogIA0KYGBgDQoNCldoaWxlIGJ1cyBzZXJ2aWNlcyBhcmUgaW50ZWdyYWwgdG8gUGhpbGFkZWxwaGlhJ3MgdHJhbnNpdCBzeXN0ZW0sIG5vdCBhbGwgYXJlYXMgYmVuZWZpdCBlcXVhbGx5LiBEYXRhIHNwZWNpZmljYWxseSBwZXJ0YWluaW5nIHRvIHN1YnVyYmFuIHJlZ2lvbnMgZHVyaW5nIHdlZWtkYXkgQU0gYW5kIFBNIHBlYWsgaG91cnMgcmV2ZWFscyBhIHN0YXJrIGRpc3Bhcml0eTogbW9zdCBzdWJ1cmJhbiBidXMgc3RhdGlvbnMgZmFjZSB3YWl0aW5nIHRpbWVzIHJhbmdpbmcgZnJvbSAzMCB0byA2MCBtaW51dGVzIGJldHdlZW4gYnVzZXMuIFRoaXMgc2lnbmlmaWNhbnQgd2FpdCB0aW1lIG5vdCBvbmx5IHVuZGVyc2NvcmVzIHRoZSBjaGFsbGVuZ2VzIGZhY2VkIGJ5IGNvbW11dGVycyBsaWtlIE1pZGdlIGJ1dCBhbHNvIGhpZ2hsaWdodHMgYSBicm9hZGVyIGlzc3VlIGFmZmVjdGluZyBtYW55IHN1YnVyYmFuIHJlc2lkZW50cyBhY3Jvc3MgU291dGhlYXN0ZXJuIFBlbm5zeWx2YW5pYSwgd2hvIHJlbHkgaGVhdmlseSBvbiBidXMgdHJhbnNpdC4gIA0KDQpgYGB7ciBzZXQgdXAgc3RvcHMgY2F0ZWdvcnksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIHJlc3VsdHMgPSAnaGlkZSd9DQojIFNFVCBVUCBTVE9QUyBDQVRFR09SWS4gVGhpcyBzdGVwIGlzIGNydWNpYWwgYXMgd2UnbGwgdXNlIHN0b3BzIGluZm9ybWF0aW9uIGFuZCBqb2luIHRoZW0gZm9yIGVhY2gga2luZCBvZiBtYXBzIA0KDQojIENvbnZlcnQgc3RvcHMgZGF0YSB0byBzZiBvYmplY3QNCnN0b3BzIDwtIHN0b3BzICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJzdG9wX2xvbiIsICJzdG9wX2xhdCIpLCBjcnMgPSA0MzI2LCByZW1vdmUgPSBGQUxTRSkNCg0KIyBMb2FkIFBBIGFuZCBTRVBUQSByZWdpb24gbWFwIGZvciBiYXNlIG1hcA0KUEEgPC0gc3RfcmVhZCgiZGF0YS9QYUNvdW50eTIwMjRfMDUuZ2VvanNvbiIpDQpTRVBUQV9yZWdpb25fbWFwIDwtIFBBW1BBJENPVU5UWV9OQU0gJWluJSBjKCJNT05UR09NRVJZIiwgIkJVQ0tTIiwgIlBISUxBREVMUEhJQSIsICJERUxBV0FSRSIsICJDSEVTVEVSIiksIF0gJT4lDQogIHNlbGVjdChQQV9DVFlfQ09ELCBDT1VOVFlfTkFNLCBGSVBTX0NPVU5UKQ0KDQojIExvYWQgQ2VudGVyIENpdHkgYm91bmRhcnkNCmNlbnRlcl9jaXR5IDwtIHN0X3JlYWQoImRhdGEvMjAyNDA3MDhfQ2VudGVyQ2l0eS9DZW50ZXJDaXR5LnNocCIpDQoNCiMgRW5zdXJlIGFsbCBkYXRhc2V0cyBhcmUgaW4gdGhlIHNhbWUgQ1JTDQpTRVBUQV9yZWdpb25fbWFwIDwtIHN0X3RyYW5zZm9ybShTRVBUQV9yZWdpb25fbWFwLCBjcnMgPSBzdF9jcnMoc3RvcHMpKQ0KY2VudGVyX2NpdHkgPC0gc3RfdHJhbnNmb3JtKGNlbnRlcl9jaXR5LCBjcnMgPSBzdF9jcnMoc3RvcHMpKQ0KDQojIFBlcmZvcm0gdGhlIHNwYXRpYWwgaW50ZXJzZWN0aW9uIGZvciBjb3VudHkNCnN0b3BzIDwtIHN0b3BzICU+JQ0KICBzdF9qb2luKFNFUFRBX3JlZ2lvbl9tYXAsIGxlZnQgPSBUUlVFLCBqb2luID0gc3RfaW50ZXJzZWN0cykgJT4lDQogIHJlbmFtZShjb3VudHkgPSBDT1VOVFlfTkFNKSAlPiUNCiAgbXV0YXRlKGNlbnRlcl9jaXR5ID0gc3Rfd2l0aGluKHN0b3BzLCBjZW50ZXJfY2l0eSwgc3BhcnNlID0gRkFMU0UpKSU+JQ0KICBtdXRhdGUoY2VudGVyX2NpdHkgPSByb3dTdW1zKGNlbnRlcl9jaXR5KSA+IDApICAjIENvbnZlcnQgbG9naWNhbCBtYXRyaXggdG8gdmVjdG9yDQoNCiMgQ2F0ZWdvcml6ZSB0aGUgc3RvcHMNCnN0b3BzIDwtIHN0b3BzICU+JQ0KICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oDQogICAgY291bnR5ID09ICJQSElMQURFTFBISUEiICYgY2VudGVyX2NpdHkgPT0gVFJVRSB+ICJQSElMQURFTFBISUEtQ2VudGVyX0NpdHkiLA0KICAgIGNvdW50eSA9PSAiUEhJTEFERUxQSElBIiAmIGNlbnRlcl9jaXR5ID09IEZBTFNFIH4gIlBISUxBREVMUEhJQS1Ob3RfQ2VudGVyX0NpdHkiLA0KICAgIFRSVUUgfiBjb3VudHkNCiAgKSkgJT4lDQogIHNlbGVjdCgtImNlbnRlcl9jaXR5IikNCiAgDQpgYGANCg0KYGBge3IgaG91cmx5IHN0b3AgZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgcmVzdWx0cyA9ICdoaWRlJ30NCiMgU0VUIFVQIEhPVVJMWSBBVkVSQUdFIFRSSVAgREFUQSBGT1IgRUFDSCBCVVMgU1RPUA0KDQpkYXRfdHJpcF9jb3VudF9ociA8LSBkYXQgJT4lDQogIGZpbHRlcihtb2RlICVpbiUgYygiQnVzIiwgIlRyb2xsZXlidXMiLCAiVHJvbGxleSIpKSAlPiUNCiAgZ3JvdXBfYnkoc3RvcF9pZCwgbW9kZSwgZmluYWxfZGF5X3R5cGUpICU+JQ0KICBzdW1tYXJpc2UodHJpcF9jb3VudCA9IG4oKSwgDQogICAgICAgICAgICBBbGxfUm91dGVzID0gcGFzdGUodW5pcXVlKHJvdXRlX2lkKSwgY29sbGFwc2UgPSAiLCAiKSwNCiAgICAgICAgICAgIC5ncm91cHMgPSAnZHJvcCcpICU+JQ0KICBtdXRhdGUoYXZnX3RyaXBfaHIgPSByb3VuZCh0cmlwX2NvdW50LzI0KSkgJT4lDQogIG11dGF0ZShsb2dfYXZnX3RyaXBfaHIgPSByb3VuZChsb2cxcChhdmdfdHJpcF9ociksIGRpZ2l0cyA9IDApKSAlPiUNCiAgbXV0YXRlKG1heF9mcmVxID0gaWZlbHNlKGF2Z190cmlwX2hyID49IDEyLCAiNSBNQVgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlIChhdmdfdHJpcF9ociA+PSA2LCAiMTAgTUFYIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSAoYXZnX3RyaXBfaHIgPj0gNCwgIjE1IE1BWCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGF2Z190cmlwX2hyID49IDIsICIzMCBNQVgiLCAiNjAgTUFYIikpKSkpDQoNCmRhdF90cmlwX2NvdW50X2hyIDwtIGxlZnRfam9pbihkYXRfdHJpcF9jb3VudF9ociwgc3RvcHMsIGJ5ID0gInN0b3BfaWQiKQ0KDQpkYXRfdHJpcF9jb3VudF9ociA8LSBzdF9hc19zZihkYXRfdHJpcF9jb3VudF9ociwgY29vcmRzID0gYygic3RvcF9sb24iLCAic3RvcF9sYXQiKSwgY3JzID0gNDMyNiwgcmVtb3ZlID0gRkFMU0UpDQoNCiMgc3VtKGRhdF90cmlwX2NvdW50X2hyJHRyaXBfY291bnQpICMxOTgwNTA1IDwtIEFsd2F5cyBtYWtlIHN1cmUgeW91IHJldGFpbiB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9uDQoNCmBgYA0KDQpgYGB7ciBzZXJ2aWNlIHBlcmlvZCBzdG9wIGRhdGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIHJlc3VsdHMgPSAnaGlkZSd9DQojIFNFVCBVUCBBVkVSQUdFIFRSSVAgREFUQSBQRVIgU0VSVklDRSBQRVJJT0QgRk9SIEVBQ0ggQlVTIFNUT1ANCg0KZGF0X3RyaXBfY291bnRfcHJkIDwtIGRhdCAlPiUNCiAgZmlsdGVyKG1vZGUgJWluJSBjKCJCdXMiLCAiVHJvbGxleWJ1cyIsICJUcm9sbGV5IikpICU+JQ0KICBncm91cF9ieShzdG9wX2lkLCBwZWFrX2NhdGVnb3J5LCBtb2RlLCBmaW5hbF9kYXlfdHlwZSkgJT4lDQogIHN1bW1hcmlzZSh0cmlwX2NvdW50ID0gbigpLCANCiAgICAgICAgICAgIEFsbF9Sb3V0ZXMgPSBwYXN0ZSh1bmlxdWUocm91dGVfaWQpLCBjb2xsYXBzZSA9ICIsICIpLA0KICAgICAgICAgICAgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIG11dGF0ZSgNCiAgICBob3VycyA9IGR1cmF0aW9uc1twZWFrX2NhdGVnb3J5XSwNCiAgICBhdmdfdHJpcF9ociA9IHJvdW5kKHRyaXBfY291bnQgLyBob3VycykNCiAgKSAlPiUNCiAgc2VsZWN0KC1ob3VycykgJT4lDQogIG11dGF0ZShsb2dfYXZnX3RyaXBfaHIgPSByb3VuZChsb2cxcChhdmdfdHJpcF9ociksIGRpZ2l0cyA9IDApKSAlPiUNCiAgbXV0YXRlKG1heF9mcmVxID0gaWZlbHNlKGF2Z190cmlwX2hyID49IDEyLCAiNSBNQVgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlIChhdmdfdHJpcF9ociA+PSA2LCAiMTAgTUFYIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSAoYXZnX3RyaXBfaHIgPj0gNCwgIjE1IE1BWCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGF2Z190cmlwX2hyID49IDIsICIzMCBNQVgiLCAiNjAgTUFYIikpKSkpDQoNCmRhdF90cmlwX2NvdW50X3ByZCA8LSBsZWZ0X2pvaW4oZGF0X3RyaXBfY291bnRfcHJkLCBzdG9wcywgYnkgPSAic3RvcF9pZCIpDQoNCmRhdF90cmlwX2NvdW50X3ByZCA8LSBzdF9hc19zZihkYXRfdHJpcF9jb3VudF9wcmQsIGNvb3JkcyA9IGMoInN0b3BfbG9uIiwgInN0b3BfbGF0IiksIGNycyA9IDQzMjYsIHJlbW92ZSA9IEZBTFNFKQ0KDQojIHN1bShkYXRfdHJpcF9jb3VudF9wcmQkdHJpcF9jb3VudCkgICMxOTgwNTA1IDwtIEFsd2F5cyBtYWtlIHN1cmUgeW91IHJldGFpbiB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9uDQpgYGANCg0KYGBge3IgYXZlcmFnZSBidXMgaG91cmx5IG1heGZyZXEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPUZBTFNFfQ0KU0VQVEFfcmVnaW9uX21hcCA8LSBzdF90cmFuc2Zvcm0oU0VQVEFfcmVnaW9uX21hcCwgc3RfY3JzKGRhdF90cmlwX2NvdW50X2hyKSkNCg0KIyAjIERlZmluZSBjb2xvciBwYWxldHRlDQojIHBhbCA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9IHZpcmlkaXNMaXRlOjp2aXJpZGlzKDI1Niwgb3B0aW9uID0gIkMiKSwgZG9tYWluID0gZGF0X3RyaXBfY291bnRfaHIkYXZnX3RyaXBfaHIpDQoNCiMgRGVmaW5lIHRoZSBjb2xvciBjb2RlcyBmb3IgYnVzIHJldm9sdXRpb24gYW5kIHJldmVyc2UgdGhlbQ0KYnVzX3Jldm9sdXRpb25fY29sb3JzIDwtIHJldihjKCIjQTYxMjNBIiwgIiM2NTVCQTYiLCAiIzI2QTY5OSIsICIjRjJBRTJFIiwgIiNGMkU1RDUiKSkNCg0KIyBFbnN1cmUgbWF4X2ZyZXEgaXMgYSBmYWN0b3Igd2l0aCBsZXZlbHMgbWF0Y2hpbmcgdGhlIGNvbG9ycw0KZGF0X3RyaXBfY291bnRfaHIkbWF4X2ZyZXEgPC0gZmFjdG9yKGRhdF90cmlwX2NvdW50X2hyJG1heF9mcmVxLCBsZXZlbHMgPSBjKCI1IE1BWCIsICIxMCBNQVgiLCAiMTUgTUFYIiwgIjMwIE1BWCIsICI2MCBNQVgiKSkNCg0KIyBEZWZpbmUgdGhlIGNvbG9yIHBhbGV0dGUgZm9yIG1heF9mcmVxDQptYXhfZnJlcV9jb2xvcnMgPC0gYygiNSBNQVgiID0gIiM0QjAwODIiLCAiMTAgTUFYIiA9ICIjRkY0NTAwIiwgIjE1IE1BWCIgPSAiI0E2MTIzQSIsICIzMCBNQVgiID0gIiMyNkE2OTkiLCAiNjAgTUFYIiA9ICIjRjJBRTJFIikNCnBhbF9tYXhfZnJlcSA8LSBjb2xvckZhY3RvcihwYWxldHRlID0gbWF4X2ZyZXFfY29sb3JzLCBkb21haW4gPSBkYXRfdHJpcF9jb3VudF9ociRtYXhfZnJlcSkNCg0KIyAjIEdlbmVyYXRlIHRoZSBsaXN0IGZvciBvdmVybGF5R3JvdXBzIHdpdGggdGhlIGRlc2lyZWQgb3JkZXINCiMgb3ZlcmxheV9ncm91cHMgPC0gZXhwYW5kLmdyaWQoZmluYWxfZGF5X3R5cGUgPSBjKCJXZWVrZGF5IiwgIldlZWtlbmQiKSwgcGVha19jYXRlZ29yeSA9IHBlYWtfb3JkZXIpICU+JQ0KIyAgIG11dGF0ZShncm91cF9uYW1lID0gcGFzdGUoZmluYWxfZGF5X3R5cGUsIHBlYWtfY2F0ZWdvcnksIHNlcCA9ICJfIikpICU+JQ0KIyAgIHB1bGwoZ3JvdXBfbmFtZSkNCg0KIyBDcmVhdGUgdGhlIGxlYWZsZXQgbWFwDQptX2F2Z190cmlwX2hyIDwtIGxlYWZsZXQoKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSBTRVBUQV9yZWdpb25fbWFwLCBjb2xvciA9ICJibGFjayIsIHdlaWdodCA9IDEsIGZpbGwgPSBGQUxTRSkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IGRhdF90cmlwX2NvdW50X2hyICU+JSBmaWx0ZXIoZmluYWxfZGF5X3R5cGUgPT0gIldlZWtkYXkiKSwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJXZWVrZGF5IiwNCiAgICAgICAgICAgICAgICAgICBsbmcgPSB+c3RvcF9sb24sIGxhdCA9IH5zdG9wX2xhdCwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5wYWxfbWF4X2ZyZXEobWF4X2ZyZXEpLA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDEsDQogICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuOCwNCiAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoIjxiPlN0b3AgSUQ6PC9iPiAiLCBzdG9wX2lkLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5TdG9wIE5hbWU6PC9iPiAiLCBzdG9wX25hbWUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxiPkF2ZyBCdXNlcy9Icjo8L2I+ICIsIGF2Z190cmlwX2hyLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5NYXggRnJlcXVlbmN5OjwvYj4gIiwgbWF4X2ZyZXEsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxiPkFsbCBSb3V0ZXM6PC9iPiAiLCBBbGxfUm91dGVzLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5EYXkgVHlwZTo8L2I+ICIsIGZpbmFsX2RheV90eXBlKSkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IGRhdF90cmlwX2NvdW50X2hyICU+JSBmaWx0ZXIoZmluYWxfZGF5X3R5cGUgPT0gIldlZWtlbmQiKSwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJXZWVrZW5kIiwNCiAgICAgICAgICAgICAgICAgICBsbmcgPSB+c3RvcF9sb24sIGxhdCA9IH5zdG9wX2xhdCwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5wYWxfbWF4X2ZyZXEobWF4X2ZyZXEpLA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDEsDQogICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuOCwNCiAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoIjxiPlN0b3AgSUQ6PC9iPiAiLCBzdG9wX2lkLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5TdG9wIE5hbWU6PC9iPiAiLCBzdG9wX25hbWUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxiPkF2ZyBCdXNlcy9Icjo8L2I+ICIsIGF2Z190cmlwX2hyLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5NYXggRnJlcXVlbmN5OjwvYj4gIiwgbWF4X2ZyZXEsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxiPkFsbCBSb3V0ZXM6PC9iPiAiLCBBbGxfUm91dGVzLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8Yj5EYXkgVHlwZTo8L2I+ICIsIGZpbmFsX2RheV90eXBlKSkgJT4lDQogIGFkZExheWVyc0NvbnRyb2woDQogICAgb3ZlcmxheUdyb3VwcyA9IGMoIldlZWtkYXkiLCAiV2Vla2VuZCIpLA0KICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkNCiAgKSAlPiUNCiAgaGlkZUdyb3VwKGMoIldlZWtlbmQiKSkgJT4lDQogIGFkZExlZ2VuZChwYWwgPSBwYWxfbWF4X2ZyZXEsIHZhbHVlcyA9IGRhdF90cmlwX2NvdW50X2hyJG1heF9mcmVxLCB0aXRsZSA9ICJNYXggRnJlcXVlbmN5IiwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gImJvdHRvbXJpZ2h0IikNCg0KbV9hdmdfdHJpcF9ocg0KYGBgDQoNCkZ1cnRoZXIgYW5hbHlzaXMgb2YgYnVzIHN0b3AgZGF0YSBieSBjb3VudHnigJRleGNsdWRpbmcgUGhpbGFkZWxwaGlhJ3MgQ2VudGVyIENpdHnigJRyZXZlYWxzIGEgbWlzbGVhZGluZyBlcXVhbGl0eSBpbiBzZXJ2aWNlIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgdGhlIGNvdW50aWVzLiBXaGlsZSB0aGUgdG9wIGJ1cyBzdG9wcyBpbiBlYWNoIGNvdW50eSBib2FzdCBhIDUtbWludXRlIG1heGltdW0gaGVhZHdheSwgcmVmbGVjdGluZyBzZWVtaW5nbHkgZXF1YWwgc2VydmljZSBsZXZlbHMsIHRoZSBhY3R1YWwgbnVtYmVyIG9mIGJ1c2VzIHRlbGxzIGEgZGlmZmVyZW50IHN0b3J5LiBGb3IgaW5zdGFuY2UsIGV2ZW4gYXQgdGhlaXIgYnVzaWVzdCwgYnVzIHN0b3BzIGluIEJ1Y2tzIENvdW50eSBzZWUgYW4gYXZlcmFnZSBvZiBvbmx5IDE1IGJ1c2VzIHBlciBob3VyIGF0IHRoZSA1IE1BWCBjYXRlZ29yeSwgc3RhcmtseSBsZXNzIHRoYW4gdGhlIDMwIGJ1c2VzIHNlZW4gaW4gc2ltaWxhciBjYXRlZ29yaWVzIGluIFBoaWxhZGVscGhpYeKAmXMgb3V0bHlpbmcgYXJlYXMuIFRoaXMgZGlzY3JlcGFuY3kgaGlnaGxpZ2h0cyB0aGUgY29uY2VudHJhdGlvbiBvZiBiZXR0ZXIgc2VydmljZXMgc3RpbGwgd2l0aGluIGNsb3NlciBwcm94aW1pdHkgdG8gdXJiYW4gY2VudGVycywgZGVzcGl0ZSBhdHRlbXB0cyBhdCBlcXVpdGFibGUgZGlzdHJpYnV0aW9uLg0KDQpgYGB7ciB0b3AgMTAgc3RvcHMgdGFibGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9DQojIEZpbHRlciBhbmQgc2VsZWN0IHRoZSB0b3AgMTAgc3RvcHMgZm9yICdtb2RlJyA9PSAnYnVzJyBvbiB0aGUgd2Vla2RheQ0KdG9wX3N0b3BzX2hyX3dlZWtkYXkgPC0gZGF0X3RyaXBfY291bnRfaHIgJT4lDQogIGZpbHRlcihtb2RlICVpbiUgYygiQnVzIiwiVHJvbGxleWJ1cyIpLCBmaW5hbF9kYXlfdHlwZSA9PSAiV2Vla2RheSIpICU+JQ0KICBzZWxlY3Qoc3RvcF9pZCwgc3RvcF9uYW1lLCBhdmdfdHJpcF9ociwgQWxsX1JvdXRlcykgJT4lDQogIGFycmFuZ2UoZGVzYyhhdmdfdHJpcF9ocikpICU+JQ0KICBzbGljZV9oZWFkKG4gPSAxMCkNCg0KIyBDcmVhdGUgYSB0YWJsZSB3aXRoIGthYmxlIGFuZCBlbmhhbmNlIGl0IHdpdGgga2FibGVFeHRyYQ0Ka2FibGUodG9wX3N0b3BzX2hyX3dlZWtkYXksICJodG1sIiwgYm9va3RhYnMgPSBUUlVFLCANCiAgICAgIGNhcHRpb24gPSAiVG9wIDEwIEJ1cyBTdG9wcyBieSBBdmVyYWdlIFRyaXBzIFBlciBIb3VyIG9uIHRoZSBXZWVrZGF5IiwNCiAgICAgIGFsaWduID0gYygnbCcsIHJlcCgnbCcsIG5jb2wodG9wX3N0b3BzX2hyX3dlZWtkYXkpIC0gMSkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICIjNjU1QkE2Iiwgd2lkdGggPSAiNWVtIikgJT4lDQogIGNvbHVtbl9zcGVjKDMsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICIjQTYxMjNBIiwgZXh0cmFfY3NzID0gInRleHQtYWxpZ246IHJpZ2h0OyIpDQoNCmBgYA0KDQoNCmBgYHtyIHRvcCA1IHN0b3BzIHRhYmxlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQ0KIyBEZWZpbmUgYSBjdXN0b20gcGFsZXR0ZSBmb3IgdGhlIGNhdGVnb3JpZXMNCmNhdGVnb3J5X2NvbG9ycyA8LSBjKA0KICAiQlVDS1MiID0gIiNBOUQwOEUiLA0KICAiQ0hFU1RFUiIgPSAiI0ZGRDk2NiIsDQogICJERUxBV0FSRSIgPSAiI0Y0QjA4NCIsDQogICJNT05UR09NRVJZIiA9ICIjRDVBNkJEIiwNCiAgIlBISUxBREVMUEhJQS1Ob3RfQ2VudGVyX0NpdHkiID0gIiNBNEMyRjQiDQopDQoNCiMgRXhhbXBsZSB3aXRoIFdlZWtkYXkgZGF0YQ0KZGF0X2ZpbHRlcmVkX3dlZWtkYXkgPC0gZGF0X3RyaXBfY291bnRfaHIgJT4lDQogIGZpbHRlcihtb2RlICVpbiUgYygiQnVzIiwiVHJvbGxleWJ1cyIpLCBmaW5hbF9kYXlfdHlwZSA9PSAiV2Vla2RheSIgJiBjYXRlZ29yeSAhPSAiUEhJTEFERUxQSElBLUNlbnRlcl9DaXR5IikNCg0KIyBGdW5jdGlvbiB0byBnZXQgdG9wIDUgc3RvcHMgcGVyIGNhdGVnb3J5DQpnZXRfdG9wX3N0b3BzX3Blcl9jYXRlZ29yeSA8LSBmdW5jdGlvbihkYXRhLCBuID0gNSkgew0KICBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGNhdGVnb3J5KSAlPiUNCiAgICBzZWxlY3Qoc3RvcF9pZCwgc3RvcF9uYW1lLCBhdmdfdHJpcF9ociwgbWF4X2ZyZXEsIEFsbF9Sb3V0ZXMsIGNhdGVnb3J5KSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoYXZnX3RyaXBfaHIpKSAlPiUNCiAgICBzbGljZV9oZWFkKG4gPSBuKSAlPiUNCiAgICB1bmdyb3VwKCkNCn0NCg0KIyBHZXQgdG9wIDUgc3RvcHMgcGVyIGNhdGVnb3J5IGZvciBXZWVrZGF5DQp0b3Bfc3RvcHNfaHJfd2Vla2RheSA8LSBnZXRfdG9wX3N0b3BzX3Blcl9jYXRlZ29yeShkYXRfZmlsdGVyZWRfd2Vla2RheSwgbiA9IDUpDQoNCiMgQ3JlYXRlIHRoZSB0YWJsZSBmb3IgV2Vla2RheSB3aXRoIHJvdyBjb2xvcnMNCmthYmxlKHRvcF9zdG9wc19ocl93ZWVrZGF5LCAiaHRtbCIsIGJvb2t0YWJzID0gVFJVRSwgDQogICAgICBjYXB0aW9uID0gIlRvcCA1IEJ1cyBTdG9wcyBieSBBdmVyYWdlIFRyaXBzIFBlciBIb3VyIG9uIHRoZSBXZWVrZGF5IiwNCiAgICAgIGFsaWduID0gYygnbCcsIHJlcCgnbCcsIG5jb2wodG9wX3N0b3BzX2hyX3dlZWtkYXkpIC0gMSkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICIjNjU1QkE2Iiwgd2lkdGggPSAiNWVtIikgJT4lDQogIGNvbHVtbl9zcGVjKDMsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICIjQTYxMjNBIiwgZXh0cmFfY3NzID0gInRleHQtYWxpZ246IHJpZ2h0OyIpIA0KYGBgDQoNCiMjIyBJbXBhY3QgYW5kIElubm92YXRpb246IEVuaGFuY2luZyBTdWJ1cmJhbiBCdXMgU2VydmljZXMNCg0KV2hpbGUgdGhlIHJhdyBkYXRhIGlsbHVtaW5hdGVzIHRoZSBzZXJ2aWNlIGRpc3Bhcml0aWVzLCB0aGUgdHJ1ZSBkZXB0aCBvZiB0aGlzIGlzc3VlIGlzIGJlc3QgY2FwdHVyZWQgdGhyb3VnaCB0aGUgbGl2ZWQgZXhwZXJpZW5jZXMgb2Ygc3VidXJiYW4gY29tbXV0ZXJzIGxpa2UgTWlkZ2UuIFBlcnNvbmFsIHN0b3JpZXMgcmV2ZWFsIHRoZSBicm9hZGVyIGh1bWFuIGltcGFjdDogbWlzc2VkIG9wcG9ydHVuaXRpZXMsIHByb2xvbmdlZCB3b3JrZGF5cywgYW5kIGRpbWluaXNoZWQgZmFtaWx5IGxpZmUgZHVlIHRvIGluZnJlcXVlbnQgYnVzIHNlcnZpY2VzLiBUaGlzIHF1YWxpdGF0aXZlIGluc2lnaHQgcGFpbnRzIGEgdml2aWQgcGljdHVyZSBvZiB0aGUgZGFpbHkgY2hhbGxlbmdlcyBhbmQgdW5kZXJzY29yZXMgdGhlIHVyZ2VuY3kgZm9yIHRhcmdldGVkIGltcHJvdmVtZW50cy4NCg0KU0VQVEEncyBvbmdvaW5nICJCdXMgUmV2b2x1dGlvbiIgcHJvamVjdCBhaW1zIHRvIG1vZGVybml6ZSBhbmQgZW5oYW5jZSBidXMgc2VydmljZXMsIHByb21pc2luZyB1cGRhdGVkIHJvdXRlcyBhbmQgaW1wcm92ZWQgZnJlcXVlbmNpZXMgdGhhdCBoYXZlbid0IGJlZW4gcmV2aXNlZCBzaW5jZSB0aGUgMTk2MHMuIFdoaWxlIHRoZSBpbml0aWF0aXZlIHNlZWtzIHRvIHJlZHVjZSBoZWFkd2F5cyB0byBhIG1heGltdW0gb2YgMzAgbWludXRlcyBldmVuIGluIHRoZSBtb3N0IHJlbW90ZSBzdWJ1cmJhbiBhcmVhcywgaXQgaGFzIGZhY2VkIGRlbGF5cyBhbmQgY3JpdGljaXNtLCBwYXJ0aWN1bGFybHkgY29uY2VybmluZyBpdHMgcG90ZW50aWFsIHRvIGRpc3Byb3BvcnRpb25hdGVseSBpbXBhY3QgbG93LWluY29tZSBuZWlnaGJvcmhvb2RzLiBUaGlzIHRlbnNpb24gaGlnaGxpZ2h0cyB0aGUgY29tcGxleCBpbnRlcnBsYXkgYmV0d2VlbiBpbXByb3Zpbmcgc2VydmljZSBlZmZpY2llbmN5IGFuZCBhZGRyZXNzaW5nIGNvbW11bml0eSBjb25jZXJucyBvZiBnZW50cmlmaWNhdGlvbi4NCg0KQXMgd2UgY29uY2x1ZGUgb3VyIGV4cGxvcmF0aW9uIG9mIHN1YnVyYmFuIGJ1cyB0cmFuc2l0IGRpc3Bhcml0aWVzIGluIFBoaWxhZGVscGhpYSwgdGhlIGNvbXBlbGxpbmcgbmFycmF0aXZlcyBvZiBkYWlseSBjb21tdXRlcnMsIHVuZGVycGlubmVkIGJ5IHJvYnVzdCBTRVBUQSBkYXRhLCBwYWludCBhIGNsZWFyIHBpY3R1cmUgb2YgdGhlIGNoYWxsZW5nZXMgYW5kIHRoZSB1cmdlbnQgbmVlZCBmb3Igc3lzdGVtaWMgaW1wcm92ZW1lbnRzLiBUaGUgIkJ1cyBSZXZvbHV0aW9uIiBwcm9qZWN0LCB3aGlsZSBhIHByb21pc2luZyBzdGVwIHRvd2FyZCBlbmhhbmNpbmcgYnVzIGZyZXF1ZW5jaWVzIGFuZCB1cGRhdGluZyBkZWNhZGVzLW9sZCByb3V0ZXMsIGFsc28gaGlnaGxpZ2h0cyB0aGUgZGVsaWNhdGUgYmFsYW5jZSByZXF1aXJlZCB0byBpbXByb3ZlIHNlcnZpY2UgZWZmaWNpZW5jeSB3aXRob3V0IGV4YWNlcmJhdGluZyBzb2NpYWwgaW5lcXVpdGllcy4NCg0KRW1icmFjaW5nIGlubm92YXRpdmUgc29sdXRpb25zIGFuZCBmb3N0ZXJpbmcgY29tbXVuaXR5IGVuZ2FnZW1lbnQgaW4gdHJhbnNpdCBwbGFubmluZyBhcmUgY3J1Y2lhbC4gQnkgY29tbWl0dGluZyB0byBib3RoIHRlY2hub2xvZ2ljYWwgdXBncmFkZXMgYW5kIGVxdWl0YWJsZSBzZXJ2aWNlIGRpc3RyaWJ1dGlvbiwgU0VQVEEgY2FuIGVuc3VyZSB0aGF0IGV2ZXJ5IGNvbW11dGVyLCByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZXkgbGl2ZSwgaGFzIHJlbGlhYmxlIGFuZCB0aW1lbHkgYWNjZXNzIHRvIHB1YmxpYyB0cmFuc2l0LiBUaGlzIG5vdCBvbmx5IGltcHJvdmVzIGluZGl2aWR1YWwgZGFpbHkgZXhwZXJpZW5jZXMgYnV0IGFsc28gc3VwcG9ydHMgYnJvYWRlciBnb2FscyBvZiBlY29ub21pYyBtb2JpbGl0eSBhbmQgc3VzdGFpbmFibGUgdXJiYW4gZGV2ZWxvcG1lbnQuDQoNCkFzIHN0YWtlaG9sZGVycywgd2UgYXJlIGNhbGxlZCB1cG9uIHRvIHN1cHBvcnQgdGhlc2UgaW5pdGlhdGl2ZXMsIGFkdm9jYXRlIGZvciB0cmFuc3BhcmVudCBhbmQgaW5jbHVzaXZlIHBsYW5uaW5nIHByb2Nlc3NlcywgYW5kIGVuc3VyZSB0aGF0IHRoZSB2b2ljZXMgb2YgYWxsIGNvbW11dGVycywgZXNwZWNpYWxseSB0aG9zZSBtb3N0IGFmZmVjdGVkIGxpa2UgTWlkZ2UsIGFyZSBoZWFyZCBhbmQgYWRkcmVzc2VkIGluIHRoZSBwdXJzdWl0IG9mIGEgdHJ1bHkgY29ubmVjdGVkIGFuZCBlcXVpdGFibGUgdHJhbnNpdCBzeXN0ZW0u